<script setup>
import {ref, reactive, watch, computed, onMounted} from "vue";
import {storeToRefs} from "pinia";
import {useUserProfile} from "@/stores/useUserProfile.js"
import {
	ready,
	dataWrite,
	softwareUpdate,
	asModuleStatusMap,
	writeChunks,
	stopRefactorByModule
} from "@/utils/refactor/index.js"
import {ArchiveTwotone} from "@vicons/material";
import {AddSquare20Filled} from "@vicons/fluent";
import {cancelProtection} from "../../../utils/refactor/index.js";
import CommandList from "../../Control/components/CommandList.vue";


const props = defineProps({
	// 是否显示自动重构弹窗
	showModal: {
		type: Boolean,
		default: false
	},
	// 重构线串口对象
	refactor: {
		type: Object,
	},
	// 光学头遥测线串口对象
	optical: {
		type: Object
	},
	// 父组件传递的,最新一条遥测数据(用于判断状态)
	latestTelemetryData: {
		type: Object
	},
	// 通知组件,用于弹出通知
	notification: {
		type: Object
	},
	// 消息组件用于短暂显示消息
	message: {
		type: Object
	},
	// 设置的发送帧的延迟
	delay: {
		type: Number,
		default: 1.7
	},
	// 字节数,
	chunkSize: {
		type: Number,
		default: 264
	},
	// 模块超时
	timeout: {
		type: Number,
		default: 30
	}
})
const emit = defineEmits({})
const {selectedVersion} = storeToRefs(useUserProfile())
// 初始重构状态
const initialRftModel = {
	// 各个文件的配置, 可以使用json配置
	fileListConfig: [
		// 主控golden,user
		{
			file: [],
			combinedArray: null,
			label: "主控golden",
			timeConsuming: 0,
			info: {
				"module": "0x11",
				"write": "0x22",
				"load": "0x11",
			},
			selected: false
		},
		{
			file: [],
			combinedArray: null,
			label: "主控user",
			timeConsuming: 0,
			info: {
				"module": "0x11",
				"write": "0x11",
				"load": "0x22",
			},
			selected: false

		},

		// 电机golden,user
		{
			file: [],
			combinedArray: null,
			label: "电机golden",
			timeConsuming: 0,
			info: {
				"module": "0x44",
				"write": "0x77",
				"load": "0x77",
			},
			selected: false

		},
		{
			file: [],
			combinedArray: null,
			label: "电机user",
			timeConsuming: 0,
			info: {
				"module": "0x44",
				"write": "0x66",
				"load": "0x88",
			},
			selected: false

		},

		// 相机golden,user
		{
			file: [],
			combinedArray: null,
			label: "跟踪相机golden",
			timeConsuming: 0,
			info: {
				"module": "0x22",
				"write": "0x44",
				"load": "0x33",
			},
			selected: false

		},
		{
			file: [],
			combinedArray: null,
			label: "跟踪相机user",
			timeConsuming: 0,
			info: {
				"module": "0x22",
				"write": "0x44",
				"load": "0x44",
			},
			selected: false

		},

		// 星敏golden,user
		{
			file: [],
			combinedArray: null,
			label: "星敏相机golden",
			timeConsuming: 0,
			info: {
				"module": "0x33",
				"write": "0x55",
				"load": "0x55",
			},
			selected: false

		},
		{
			file: [],
			combinedArray: null,
			label: "星敏相机user",
			timeConsuming: 0,
			info: {
				"module": "0x33",
				"write": "0x55",
				"load": "0x66"
			},
			selected: false

		},
	].map(item => {
			if (item.label.includes("golden")) {
				return {...item, disabled: false}
			}
			return item
		}
	),
	// 已完成的任务列表
	completedTask: [],
	// 当前步骤(1-4)
	step: 0,
	// 进度条数值
	rawPercentage: 0,
	// 任务数
	taskCount: 0,
	// 任务占比
	taskPtag: 0,
	// 阶段占比
	segmentPtag: 0,

	// 是否已经处于重构的执行中
	isRunning: false,
	// 重构时的生命周期/状态
	isReady: false,
	isWriting: false,
	isUpdating: false,
	isLoaded: false,
	// 重构任务全部完成
	complete: false
}
// 记录上传顺序
const uploadOrder = reactive(new Set());

// 响应式的重构状态
const rftModel = reactive(structuredClone(initialRftModel))

const slot = ref(null)

function addSlot() {
	const baseItem = rftModel.fileListConfig.find(item => item.label === slot.value);
	if (!baseItem) {
		console.error(`Label "${slot.value}" not found`);
		return;
	}

	// 统计已有的同类 label 个数（含基础名及带编号）
	const prefix = slot.value;
	const regex = new RegExp(`^${prefix}(\\d*)复制?$`);
	let maxSuffix = 1;

	for (const item of rftModel.fileListConfig) {
		const match = item.label.match(regex);
		if (match) {
			const suffix = match[1] ? parseInt(match[1]) : 0;
			if (suffix >= maxSuffix) {
				maxSuffix = suffix + 1;
			}
		}
	}

	// 克隆 baseItem
	const newItem = {
		...JSON.parse(JSON.stringify(baseItem)), // 深拷贝
		label: `${prefix}${maxSuffix}复制`,
		type: 'copy'
	};

	rftModel.fileListConfig.push(newItem)
}

// 重构任务列表(仅combinedArray为true则视为该文件需要重构)
const computedRftList = computed({
	get() {
		return Array.from(uploadOrder)
			.map(label => rftModel.fileListConfig.find(item => item.label === label))
			.filter(item => item); // 确保不会有空项
	},
	set(value) {
		// 设置值
		const lastIndex = rftModel.fileListConfig.findLastIndex(item => item.label === value.label)
		rftModel.fileListConfig[lastIndex] = value
	}
})


/**
 * 处理各类文件的上传行为
 * @param type 文件类型{task, ms, motor, camera, xm}
 * @param index 在rftModel.fileListConfig中的索引
 * @param file 用户上传的文件对象
 * @param label 用户上传的文件的类型(如星敏golden, ....)
 */
const handleUploadFile = async (index, {file}, label) => {
	console.log(await file.file.arrayBuffer())
	// 移除文件操作
	if (file.status === "removed") {
		rftModel.fileListConfig[index].selected = false
		rftModel.fileListConfig[index].combinedArray = null
		uploadOrder.delete(label); // 确保不会重复
		return
	}
	if (file.type !== "application/octet-stream" || !file.name.endsWith(".bin")) {
		props.message.error(`你可能选错文件了: ${file.name}`)
	}

	uploadOrder.add(label); // 记录上传顺序
	// 生成blob
	rftModel.fileListConfig[index].combinedArray = new Blob(
		[await file.file.arrayBuffer()], {type: 'application/octet-stream'}
	);
	rftModel.fileListConfig[index].selected = true
}


// 计算属性, 对进度条最终显示仅保留两位小数
const percentage = computed(() => rftModel.rawPercentage.toFixed(2))

/**
 * 格式化时间, 以秒为基准
 * @param seconds
 * @returns {any}
 */
const formatTime = (seconds) => {
	if (!seconds) return "00:00:00";

	let hours = String(Math.floor(seconds / 3600)).padStart(2, '0');
	let minutes = String(Math.floor((seconds % 3600) / 60)).padStart(2, '0');
	let secs = String(seconds % 60).padStart(2, '0');

	return `${hours}:${minutes}:${secs}`;
};

/**
 * 启动自动重构任务
 */
const startTask = async () => {
	// 检查串口连通性
	if (!props.optical.port?.readable) {
		props.message.error("没插入遥测线!")
		return
	}
	if (!props.refactor.port?.readable) {
		props.message.error("没插入重构线!")
		return
	}
	// 检查任务配置
	if (!computedRftList.value || computedRftList.value.length === 0) {
		props.message.error("任务配置无效, 没有上传任何文件!");
		return;
	}
	if (rftModel.isRunning) {
		console.error("已经处于任务状态!")
	}
	rftModel.isRunning = true
	// 任务总数
	rftModel.taskCount = computedRftList.value.length

	// 每个任务的占比16.66
	rftModel.taskPtag = 100 / rftModel.taskCount

	// 每个任务各阶段占比
	rftModel.segmentPtag = rftModel.taskPtag / 4

	// 如果是v42版本以上需要发送解锁星敏和相机的自闭环保护
	if (parseInt(selectedVersion.value.optical.telemetry.match(/\d+/)[0]) >= 42) {
		// 相机星敏自闭环保护取消
		await cancelProtection(props.optical.port.writable)
		console.log("当前遥测版本star95以上,发送了取消相机星敏自闭环保护取消.")
	}
	// 先停止掉所有模块的重构恢复初始状态
	for (const mod of ["0x11", "0x22", "0x33", "0x44"]) {
		await stopRefactorByModule(props.optical.port.writable, mod)
		await new Promise(resolve => setTimeout(resolve, 100))
	}
	console.log("已停止掉所有模块的重构!")

	// 遍历用户需要的重构项目
	for (const [index, item] of computedRftList.value.entries()) {
		// 任务执行前计时
		const startTime = performance.now()
		// 获取各个模块的标志位
		const {module, write, load} = item.info
		// 获取重构文件的buffer
		const {combinedArray, label} = item
		console.log("文件buffer:", combinedArray, label)
		// 定时更新当前 item 的 timeConsuming(只存秒数) 记录当前重构项的耗时
		let consumingInterval = setInterval(() => {
			let elapsedTime = Math.floor((performance.now() - startTime) / 1000); // 计算已过去的秒数
			if (item) {
				item.timeConsuming = elapsedTime; // 只存秒数
			}
		}, 1000); // 每秒更新一次

		// 就绪 -> (启动, 写入, 加载, 停止)
		try {
			// 依次执行每个任务
			props.notification.success({
				title: `${index + 1}.当前任务[${label}]正在执行`,
				content: `时间:${new Date().toLocaleTimeString()}`
			})

			// 等待每个单项任务执行完毕
			await runTask({
				label, module, write, load, combinedArray
			})
			clearInterval(consumingInterval) // 清除定时器
		} catch (e) {
			// 出现异常跳过,并且停止当前项目重构
			await stopRefactorByModule(props.optical.port.writable, module)
			console.error(e)
			props.notification.error({title: e, content: `时间:${new Date().toLocaleTimeString()}`})
			clearInterval(consumingInterval) // 清除定时器
		}
	}
	rftModel.complete = true
	props.notification.success({title: "所有重构项目均已完成", content: `时间:${new Date().toLocaleTimeString()}`})
	rftModel.isRunning = false
}
const moduleRespMap = computed(() => {
	return {
		"0x11": props.latestTelemetryData.parsedData.find(item => item.label === "主控重构状态"),
		"0x44": props.latestTelemetryData.parsedData.find(item => item.label === "电机重构状态"),
		"0x33": props.latestTelemetryData.parsedData.find(item => item.label === "星敏重构状态"),
		"0x22": props.latestTelemetryData.parsedData.find(item => item.label === "相机重构状态")
	}
})

/**
 * 执行单个任务
 * @param label 当前模块名称 ex:星敏user,电机golden
 * @param module 当前重构模块
 * @param write 当前写入模块
 * @param load 当前加载模块
 * @param timeConsuming 耗时
 * @param timeout 设置当前任务超时时间, 默认半小时30min
 */
const runTask = async ({label, module, write, load, combinedArray, timeout = 60000 * props.timeout}) => {
	return new Promise(async (resolve, reject) => {
		// 设置计时器(超时操作)
		const timer = setTimeout(async () => {
			// 停止当前模块的重构
			await stopRefactorByModule(props.optical.port.writable, module)
			reject(`当前模块${label},重构超出设定时间${timeout / 1000 / 60}分钟自动跳过!`);
		}, timeout);

		console.log(label, module, write, load, timeout, "所有参数集合!")


		// 统一清理函数
		const cleanup = () => {
			clearTimeout(timer)
		}

		try {
			// 前置准备(切重构模式)
			await ready(props.optical.port.writable, module)

			// 系统模式监听(主控重构完毕会自动跳存储)
			const sysModeWatcher = watch(() => props.latestTelemetryData, async (newVal) => {
				const state = newVal.parsedData.find(item => item.label === "激光终端状态")
				// 防止主控重构后模式变成待机, 判断状态是否变成存储,如果存储则切回重构
				if (!rftModel.complete && state && parseInt(state.sourceCode) !== parseInt("0xf")) {
					await ready(props.optical.port.writable, module)
					props.message.info("重新切回重构模式")
				}
			})

			// 开始监视(等待 -> 重构就绪, 固件升级准备完成等状态)
			const readyWatcher = watch(() => props.latestTelemetryData, async (newVal) => {
				if (props.refactor && rftModel.step === 0) {
					// 检查前置条件(固件升级准备完成,重构就绪等)
					const condition = asModuleStatusMap[module]["wait"]
					// 检查状态,准备时要检查很多状态
					for (const [key, value] of Object.entries(condition)) {
						rftModel.isReady = parseInt(newVal.parsedData.find(item => item.label === key).sourceCode) === parseInt(value);
					}
					if (rftModel.isReady) {
						props.message.success("准备就绪")
						// 创建读取器读取blob中的数据
						const reader = combinedArray.stream().getReader();
						// 获取串口写入器(重构线)
						const writer = props.refactor.port.writable.getWriter();

						// 发送数据帧
						await writeChunks(reader, writer, combinedArray, props.chunkSize, props.delay);
						props.notification.success({
							title: `[${label}]数据帧全部写入完成!`,
							content: `时间:${new Date().toLocaleTimeString()}`
						})
						rftModel.step += 1

						rftModel.rawPercentage = rftModel.rawPercentage + rftModel.segmentPtag
					}
				}

			})

			// 接收数据帧监视(等待 -> 接收ok)
			const receiveWatcher = watch(() => props.latestTelemetryData, async (newVal) => {
				if (combinedArray && rftModel.step === 1) {
					readyWatcher() // 清除上一个监视器
					// 通过主控判断状态
					if (parseInt(moduleRespMap.value["0x11"].sourceCode) === parseInt("0x4")) {
						// 接收ok开始重构写入
						await dataWrite(props.optical.port.writable, write)
						props.notification.success({
							title: `[${label}]软件写入中...`,
							content: `时间:${new Date().toLocaleTimeString()}`
						})
						rftModel.isWriting = true
						rftModel.step += 1
						rftModel.rawPercentage = rftModel.rawPercentage + rftModel.segmentPtag
					}
				}
			})

			// 软件更新监视(等待 -> 加载OK)
			const softwareUpdateWatcher = watch(() => props.latestTelemetryData, async (newVal) => {
				if (combinedArray && rftModel.step === 2) {
					receiveWatcher() // 清除上一个监视器
					// 固件升级完成
					console.log(moduleRespMap.value[module].sourceCode.toUpperCase(), asModuleStatusMap[module]["written"].toUpperCase())
					// NOTE: 模块级别判断(星敏,相机) star95后相机星敏出现不跳 (固件升级完成) 而跳未重构 所以放行未重构状态0x0
					const sourceCodeInt = parseInt(moduleRespMap.value[module].sourceCode);
					const writtenInt = parseInt(asModuleStatusMap[module]["written"]);
					console.log(sourceCodeInt, writtenInt)
					// (sourceCodeInt === writtenInt || sourceCodeInt === 0) &&
					// 未加载 and 写入完成(星敏和相机判断会放行未重构)
					// if (
					// 	!rftModel.isUpdating &&
					// 	(
					// 		(label.includes("相机") || label.includes("星敏"))
					// 			? (sourceCodeInt === writtenInt || sourceCodeInt === 0)
					// 			: (sourceCodeInt === writtenInt)
					// 	)
					// ) {
					if ((parseInt(moduleRespMap.value[module].sourceCode) === parseInt(asModuleStatusMap[module]["written"]) && !rftModel.isUpdating)) {
						// 此时开始加载
						rftModel.isUpdating = true
						// cmd:开始加载光学头软件
						await softwareUpdate(props.optical.port.writable, load)
						props.notification.success({
							title: `[${label}]软件加载中...`,
							content: `时间:${new Date().toLocaleTimeString()}`
						})
						console.log(moduleRespMap.value[module].value, asModuleStatusMap[module]["written"].toUpperCase(), !rftModel.isUpdating)
						console.log("写入ok")
						rftModel.step += 1
						rftModel.rawPercentage = rftModel.rawPercentage + rftModel.segmentPtag
					}
				}
			})

			// 重构完成监视(等待 -> 未重构)则表示流程结束
			const completeWatcher = watch(() => props.latestTelemetryData, async (newVal) => {
				if (combinedArray && rftModel.step === 3) {
					softwareUpdateWatcher() // 清除上一个监视器
					// 重构完成 -> 当前模块状态变回预设状态且软件加载完毕
					if (parseInt(moduleRespMap.value[module].sourceCode) === parseInt(asModuleStatusMap[module]["finished"]) && !rftModel.isLoaded) {
						props.notification.success({
							title: `[${label}]重构完成`,
							content: `时间:${new Date().toLocaleTimeString()}`
						})
						// 更新任务栏重构进度
						rftModel.completedTask.push(label)
						console.log("加载ok")
						rftModel.isLoaded = true
						console.log(moduleRespMap.value[module].value, !rftModel.isLoaded)
						rftModel.step += 1
						// 清空各种状态
						rftModel.step = 0
						rftModel.isReady = false
						rftModel.isWriting = false
						rftModel.isUpdating = false
						rftModel.isLoaded = false
						// 停止当前模块的重构
						await stopRefactorByModule(props.optical.port.writable, module)
						// 更新任务进度
						rftModel.rawPercentage = rftModel.rawPercentage + rftModel.segmentPtag
						// 在所有监视器完成后结束任务
						cleanup()
						completeWatcher() // 清除监视器
						// 重构完成后结束整个promise
						resolve()
					}
				}
			})
		} catch (e) {
			// 清理定时器操作
			reject(`当前模块${label},重构发生未知异常${e}`);
			cleanup()
		}
	})
}
// 重置重构相关状态
const resetRft = () => {
	userFList.value = []
	Object.assign(rftModel, structuredClone(initialRftModel));
	// 保证combinedArr被清空
	uploadOrder.clear()
	props.notification.destroyAll()
	console.log(rftModel)
}
// modal---------------------------------------
const localShowModal = ref(props.showModal)
const softwareVersion = ref('新硬件')
watch(() => props.showModal, (newVal) => {
	localShowModal.value = newVal
})
onMounted(() => {
	console.log(selectedVersion.value)
})

function filterFiles(fileList) {
	const result = [];

	fileList.forEach(file => {
		const path = file.fullPath;
		const parts = path.split('/').filter(Boolean);

		// 支持主控、电机（主控/User、主控/Golden 等）
		for (let i = 0; i < parts.length - 1; i++) {
			if (["主控", "电机"].includes(parts[i]) && ["User", "Golden"].includes(parts[i + 1])) {
				result.push({
					label: `${parts[i]}${parts[i + 1].toLowerCase()}`,
					file
				});
				return;
			}
		}

		// 支持 星敏相机 / 跟踪相机 / 【新硬件】xxx / User|Golden
		for (let i = 0; i < parts.length - 2; i++) {
			if (
				["星敏相机", "跟踪相机"].includes(parts[i]) &&
				parts[i + 1].includes(softwareVersion.value) &&
				["User", "Golden"].includes(parts[i + 2])
			) {
				result.push({
					label: `${parts[i]}${parts[i + 2].toLowerCase()}`,
					file
				});
				return;
			}
		}
	});

	return result;
}

// 上传的目录中文件
const userFList = ref([])
const handleFinalUpload = async () => {
	const flist = filterFiles(userFList.value)

	for (const f of flist) {
		const idx = rftModel.fileListConfig.findIndex(each => each.label === f.label)
		if (idx >= 0) {
			rftModel.fileListConfig[idx].file = [f.file]
			uploadOrder.add(f.label)
			rftModel.fileListConfig[idx].combinedArray = new Blob(
				[await f.file.file.arrayBuffer()],
				{type: 'application/octet-stream'}
			)
			rftModel.fileListConfig[idx].selected = true
		}
	}
}

watch(userFList, async () => {
	await handleFinalUpload(userFList.value)
})
</script>

<template>
	<n-modal
		v-model:show="localShowModal"
		:on-update:show="() => emit('update:showModal')"
		class="w-11/12"
		preset="card"
		size="huge"
		closable
		display-directive="show"
	>
		<template #header>
			<n-text>配置自动重构</n-text>
			<n-text depth="3">(上传对应模块的重构文件,左侧会自动勾选!记得先链接串口)</n-text>
		</template>
		<template #header-extra>
			退出
		</template>

		<template #default>
			<n-flex class="flex gap-2 flex-nowrap items-center">
				<n-flex class="w-1/8 flex-col" justify="center">

					<n-text class="text-red-300">此处无需选择,</n-text>
					<n-text class="text-red-300">上传文件会自动勾选.</n-text>
					<n-space item-style="display: flex;" class="pointer-events-none">
						<n-checkbox v-for="item in rftModel.fileListConfig"
									disabled
									tabindex="-1"
									v-model:checked="item.selected"
									:label="item.label">
						</n-checkbox>
					</n-space>
					<n-upload
						:input-props="{ webkitdirectory: true , directory:true}"
						directory-dnd directory v-model:file-list="userFList"
						class="w-full"
						:show-file-list="false">
						<n-button class="w-40">上传重构目录</n-button>
					</n-upload>
					<div>
						<n-radio-group v-model:value="softwareVersion">
							<n-radio value="新硬件">新硬件</n-radio>
							<n-radio value="旧硬件">旧硬件</n-radio>
						</n-radio-group>
					</div>

				</n-flex>
				<n-divider class="h-72" vertical/>
				<div class="flex flex-wrap w-7/8 items-center" id="n-upload-group">
					<n-upload
						v-for="(item, index) in rftModel.fileListConfig"
						v-model:file-list="item.file"
						:max="1"
						@change="(event) => handleUploadFile(index, event, item.label)"
						class="xl:w-1/4 lg:w-1/3 sm:w-1/3 h-1/2"
						file-list-class="text-xs mt-0 px-1"
						trigger-class="px-1 py-1"
						:disabled="item.disabled"
					>
						<n-upload-dragger class="relative sm:px-0">
							<div class="flex items-center w-full justify-center">

								<n-icon class="" size="48" :depth="3"
										:class="[rftModel.completedTask.includes(item.label)? 'text-green-700':'']">
									<ArchiveTwotone/>
								</n-icon>
								<n-text class="absolute right-2 top-1.5 font-bold" v-if="item.timeConsuming">
									耗时:{{ formatTime(item.timeConsuming) }}
								</n-text>
							</div>
							<n-text>
								上传一个bin文件用于
								<n-text :class="item.label.includes('user') ? 'text-green-400/70' : 'text-red-400'">
									{{ item.label }}
								</n-text>
							</n-text>
						</n-upload-dragger>
					</n-upload>
					<!-- 非循环元素 -->
					<div
						class="xl:w-1/4 lg:w-1/3 sm:w-1/3 p-1 h-32">
						<div class="bg-zinc-700/50 flex h-full hover:border border-emerald-300 border-dashed flex-col
						items-center justify-center rounded-sm text-gray-400 gap-2">
							<n-select class="w-36" v-model:value="slot"
									  :options="rftModel.fileListConfig.filter(f=>f.type!=='copy')"
									  value-field="label"/>
							<n-button class="p-4" @click="addSlot">
								<template #icon>
									<AddSquare20Filled class="text-zinc-400"/>
								</template>
								添加一个槽位
							</n-button>
						</div>
					</div>
				</div>
			</n-flex>
		</template>

		<template #footer>
			<n-flex>
				{{ rftModel.completedTask.length ? "已完成:" : "" }}
				<n-text type="warning" v-for="item in rftModel.completedTask">{{ item }}</n-text>
			</n-flex>
			<n-progress processing type="line" :percentage="percentage" class="text-nowrap"/>
		</template>

		<template #action>
			<n-flex class="w-full">
				{{ computedRftList.length ? "重构顺序:" : "" }}
				<n-flex v-for="item in computedRftList">
					<n-text class="text-blue-500">{{ `${item.label}` }}</n-text>
					|
				</n-flex>
				<n-button class="ms-auto" @click="resetRft">重置</n-button>
				<n-button type="success" @click="startTask" :disabled="rftModel.isRunning">启动自动重构</n-button>
			</n-flex>
		</template>
	</n-modal>
</template>

<style>

</style>